package com.sys.websocket;

import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.StreamGobbler;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.sys.utils.Utils;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Executors;


@ServerEndpoint(value = "/sys/webSocketShell", configurator = WebSocketConfig.class)
@Component
public final class ShellWebSocketServer {

    //concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。
    private static CopyOnWriteArraySet<ShellWebSocketServer> webSocketSet = new CopyOnWriteArraySet<ShellWebSocketServer>();

    //与某个客户端的连接会话，需要通过它来给客户端发送数据
    private Session session;

    private String hostname = ""; // 连接ip
    private String username = ""; // 连接用户名
    private String password = ""; // 连接密码
    private String key = HOST_NAME; // 当前步骤
    private final static String HOST_NAME = "hostname";
    private final static String USER_NAME = "username";
    private final static String PASS_WORD = "password";

    // 连接信息
    private Connection conn = null;
    // term的session信息
    private ch.ethz.ssh2.Session sshSession = null;
    private OutputStream fout = null;

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        webSocketSet.add(this);     //加入set中

        sendMessage(ShellWebSocketServer.HOST_NAME + ":");
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        this.disconnect();
        webSocketSet.remove(this);  //从set中删除
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message) {
        // %0D%0Alogout%0D%0A
        if (fout == null) {
            try {
                String unicode = JSON.toJSONString(message, SerializerFeature.BrowserCompatible);
                if (unicode.indexOf("\"\\u001B") != -1) return;
                if (key.equals(ShellWebSocketServer.HOST_NAME)) {
                    if (message.equals("\r")) {
                        key = ShellWebSocketServer.USER_NAME;
                        sendMessage("\n\r" + ShellWebSocketServer.USER_NAME + ":");
                    } else if (unicode.equals("\"\\u007F\"")) {
                        if (hostname.length() != 0) {
                            hostname = hostname.substring(0, hostname.length() - 1);
                            sendMessage("\u0008\u001B\u005BK");
                        }
                    } else {
                        hostname += message;
                    }
                } else if (key.equals(ShellWebSocketServer.USER_NAME)) {
                    if (message.equals("\r")) {
                        key = ShellWebSocketServer.PASS_WORD;
                        sendMessage("\n\r" + ShellWebSocketServer.PASS_WORD + ":");
                    } else if (unicode.equals("\"\\u007F\"")) {
                        if (username.length() != 0) {
                            username = username.substring(0, username.length() - 1);
                            sendMessage("\u0008\u001B\u005BK");
                        }
                    } else {
                        username += message;
                    }
                } else {
                    if (message.equals("\r")) {
                        this.connect();
                    } else if (unicode.equals("\"\\u007F\"")) {
                        if (password.length() != 0) {
                            password = password.substring(0, password.length() - 1);
                            sendMessage("\u0008\u001B\u005BK");
                        }
                    } else {
                        password += message;
                    }
                }

                if (!message.equals("\r") && !unicode.equals("\"\\u007F\"")) {
                    sendMessage(new String(message.getBytes("ISO-8859-1"), "UTF-8"));
                }
            } catch (IOException e) {
                Utils.loggerError(this, e);
            }
        } else {
            this.write(message);
        }
    }


    /**
     * @param error
     */
    @OnError
    public void onError(Throwable error) {
        this.disconnect();
        webSocketSet.remove(this);     //加入set中
        Utils.loggerError(this, error);
    }

    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message) {
        try {
            this.session.getBasicRemote().sendText(message);
            if (JSON.toJSONString(message, SerializerFeature.BrowserCompatible).equals("\"\\r\\n\\u767B\\u51FA\\r\\n\"") ||
                    JSON.toJSONString(message, SerializerFeature.BrowserCompatible).equals("\"\\r\\nlogout\\r\\n\"")) {

                session.close();
                this.disconnect();
            }
        } catch (Exception e) {
            Utils.loggerError(this, e);
        }
    }


    /**
     * 连接到目标服务器
     *
     * @return
     */
    public void connect() {
        try {
            // 建立连接
            conn = new Connection(hostname, 22);
            // 连接
            conn.connect();
            // 授权
            boolean isAuthenticate = conn.authenticateWithPassword(username, password);
            if (isAuthenticate) {
                sendMessage("\n\r");

                // 打开连接
                sshSession = conn.openSession();
                InputStream is = new StreamGobbler(sshSession.getStdout());
                startWriter(is); // 设置读取流
                sshSession.requestPTY("xterm", 90, 30, 0, 0, null);
                // session.requestDumbPTY();  //建立虚拟终端
                sshSession.startShell();  //打开一个shell
                fout = sshSession.getStdin();

            }
        } catch (Exception e) {
            Utils.loggerError(this, e);
        }
    }

    /**
     * 发送cmd
     *
     * @param is
     */
    public void startWriter(InputStream is) {
        Executors.newCachedThreadPool().execute(() -> {
            try {
                byte[] buff = new byte[8192];
                int len;
                while ((len = is.read(buff)) > 0) {
                    //设定从0 开始
                    StringBuffer sb = new StringBuffer();

                    //读取数组里面的数据，进行补码
                    for (int i = 0; i < len; i++) {
                        //进行补码操作
                        char c = (char) (buff[i] & 0xff);
                        sb.append(c);
                    }
                    this.sendMessage(new String(sb.toString().getBytes("ISO-8859-1"), "UTF-8"));
                }
                is.close();
            } catch (Exception e) {
                Utils.loggerError(this, e);
            }
        });
    }

    /**
     * 写数据到服务器端，让机器执行命令
     *
     * @param cmd 命令
     * @return
     */
    public void write(String cmd) {
        Executors.newCachedThreadPool().execute(() -> {
            try {
                fout.write(cmd.getBytes());
            } catch (IOException e) {
                this.disconnect();
            }
        });
    }

    /**
     * 关闭连接
     */
    public void disconnect() {
        try {
            //将与服务器端的连接关闭掉，并设置为空
            if (conn != null) {
                conn.close();
                conn = null;
            }
            if (session != null) {
                session.close();
                session = null;
            }
            if (fout != null) {
                fout.close();
                fout = null;
            }
        } catch (Exception e) {
            Utils.loggerError(this, e);
        }
    }

}
